```vue
<template>
    <div class="chat-main">
        <!-- 消息容器 -->
        <div class="chat-window">
            <div class="message-container" ref="messageContainer">
                <div v-if="messages.length === 0" class="empty-state">暂无消息</div>
                <div v-for="(msg, index) in allMessages" :key="msg.id || index" class="message-item">
                    <!-- 用户名头部 -->
                    <div class="message-header">
                        <span class="sender" :class="{ user: msg.role === 'user', ai: msg.role !== 'user' }">
                            {{ msg.role === 'user' ? '你' : 'AI' }}
                        </span>
                    </div>
                    
                    <!-- 显示思考过程（如果存在） -->
                    <div v-if="hasThinkingContent(msg) || (msg.isStreaming && msg.thinking)" class="thinking-block">
                        <div class="thinking-header" @click="toggleThinking(msg)" style="cursor: pointer; z-index: 10;">
                            <span class="thinking-toggle-icon">
                                {{ getThinkingExpanded(msg) ? '▼' : '▶' }}
                            </span>
                            <span class="thinking-toggle-text">
                                点击{{ getThinkingExpanded(msg) ? '收起' : '展开' }}思考过程
                                <span v-if="msg.isStreaming" class="streaming-indicator">
                                    <span class="dot"></span>
                                    <span class="dot"></span>
                                    <span class="dot"></span>
                                </span>
                            </span>
                        </div>
                        <div v-show="getThinkingExpanded(msg)" class="thinking-content-container">
                            <pre class="thinking-content">{{ getThinkingContent(msg) }}</pre>
                        </div>
                    </div>
                    
                    <!-- Markdown 内容 -->
                    <div v-if="msg.content" class="content markdown-content"
                        :class="{ user: msg.role === 'user', ai: msg.role !== 'user' }"
                        v-html="renderMarkdown(msg.content)">
                    </div>
                </div>
            </div>
            
            <!-- 输入框 -->
            <div class="input-area">
                <div class="input-wrapper">
                    <el-input 
                        v-model="inputMessage" 
                        placeholder="输入你的问题..." 
                        @keyup.enter="sendMessageTOLLM" 
                        clearable
                        :rows="1" 
                        type="textarea" 
                        :autosize="{ minRows: 1, maxRows: 4 }" 
                    />
                    <el-button 
                        @click="sendMessageTOLLM" 
                        class="send-button"
                        :disabled="!inputMessage.trim() || sending"
                    >
                        {{ sending ? '发送中...' : '发送' }}
                    </el-button>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick, computed } from 'vue'
import { useRoute } from 'vue-router'
import { getMessage, sendMessage, startStreamReply } from '@/api/chat'

// 引入 marked 和 DOMPurify
import { marked } from 'marked'
import DOMPurify from 'dompurify'

const route = useRoute()
const sessionId = ref('1')

const messages = ref([])
const inputMessage = ref('')
const messageContainer = ref(null)
const sending = ref(false)

// 流式消息处理
const streamingMessage = ref(null)
let eventSource = null

// 生成唯一ID的辅助函数
const generateId = () => Date.now() + Math.random()

// 计算属性：合并历史消息和流式消息
const allMessages = computed(() => {
    const msgs = messages.value.map(msg => ({ ...msg }))
    
    // 如果有流式消息，添加到末尾
    if (streamingMessage.value) {
        msgs.push({ ...streamingMessage.value })
    }
    
    return msgs
})

// Helper function to check if a message has thinking content
const hasThinkingContent = (msg) => {
    if (!msg) return false
    // For streaming message, check the actual streamingMessage ref
    if (msg.id && typeof msg.id === 'string' && msg.id.startsWith('stream-')) {
        return streamingMessage.value && streamingMessage.value.thinking && streamingMessage.value.thinking.trim() !== ''
    }
    // For regular messages
    return msg.thinking && msg.thinking.trim() !== ''
}

// Helper function to get thinking content
const getThinkingContent = (msg) => {
    if (!msg) return ''
    if (msg.id && typeof msg.id === 'string' && msg.id.startsWith('stream-')) {
        return streamingMessage.value ? streamingMessage.value.thinking : ''
    }
    return msg.thinking || ''
}

// 渲染 Markdown 函数
const renderMarkdown = (text) => {
    if (!text) return ''
    try {
        const parsed = marked.parse(text)
        return DOMPurify.sanitize(parsed)
    } catch (error) {
        console.error('Error parsing markdown:', error)
        return DOMPurify.sanitize(text)
    }
}

// 获取消息的展开状态
const getThinkingExpanded = (msg) => {
    if (!msg) return false
    if (msg.id && typeof msg.id === 'string' && msg.id.startsWith('stream-')) {
        return streamingMessage.value ? streamingMessage.value.thinkingExpanded : false
    }
    return msg.thinkingExpanded || false
}

// 设置消息的展开状态
const setThinkingExpanded = (msg, expanded) => {
    if (!msg) return
    if (msg.id && typeof msg.id === 'string' && msg.id.startsWith('stream-')) {
        if (streamingMessage.value) {
            streamingMessage.value.thinkingExpanded = expanded
        }
    } else {
        // 确保在Vue的响应式系统中更新
        const msgIndex = messages.value.findIndex(m => m.id === msg.id)
        if (msgIndex !== -1) {
            messages.value[msgIndex].thinkingExpanded = expanded
        } else {
            // 直接更新消息对象（如果它不是响应式的）
            msg.thinkingExpanded = expanded
        }
    }
}

// 滚动到底部
const scrollToBottom = () => {
    nextTick(() => {
        if (messageContainer.value) {
            messageContainer.value.scrollTop = messageContainer.value.scrollHeight
        }
    })
}

// 获取历史消息
const fetchMessages = async () => {
    try {
        const res = await getMessage(sessionId.value)
        if (res.code === 100) {
            // 为历史消息添加ID
            messages.value = res.data.map(msg => ({
                ...msg,
                id: msg.id || generateId(),
                thinkingExpanded: false
            }))
            scrollToBottom()
        }
    } catch (e) {
        console.error('获取消息失败:', e)
    }
}

// 清理 SSE 连接
const cleanupSSE = () => {
    if (eventSource) {
        try {
            eventSource.close()
        } catch (e) {
            // 忽略关闭错误
        }
        eventSource = null
    }
}

// 发送消息
const sendMessageTOLLM = async () => {
    const content = inputMessage.value.trim()
    if (!content || !sessionId.value || sending.value) return

    // 添加用户消息
    messages.value.push({ 
        id: generateId(),
        role: 'user', 
        content,
        thinkingExpanded: false
    })
    
    // 重置输入框和发送状态
    inputMessage.value = ''
    sending.value = true
    
    // 初始化流式消息对象
    streamingMessage.value = {
        id: `stream-${generateId()}`,
        role: 'assistant',
        content: '',
        thinking: '',
        thinkingExpanded: true, // 默认展开思考内容
        isStreaming: true
    }
    
    scrollToBottom()

    try {
        await sendMessage('qwen3:4b', content, sessionId.value, true)
        startStream()
    } catch (e) {
        console.error('发送失败:', e)
        sending.value = false
        // 清理流式消息
        streamingMessage.value = null
        cleanupSSE()
    }
}

const startStream = () => {
    cleanupSSE() // Clean up any existing connection

    eventSource = startStreamReply(
        sessionId.value,
        (event) => {
            try {
                let data;
                if (typeof event.data === 'string') {
                    try {
                        data = JSON.parse(event.data);
                    } catch (parseError) {
                        console.warn('非JSON数据:', event.data)
                        return
                    }
                } else {
                    data = event.data;
                }

                // 处理流结束
                if (data.done === "true" || data.done === true) {
                    // 将流式消息添加到历史消息
                if (streamingMessage.value && 
                    (streamingMessage.value.content || streamingMessage.value.thinking)) {
                    // 转换为历史消息时关闭思考内容
                    messages.value.push({
                        id: generateId(),
                        role: 'assistant',
                        content: streamingMessage.value.content,
                        thinking: streamingMessage.value.thinking,
                        thinkingExpanded: false, // 思考完成后自动关闭
                        isStreaming: false
                    })
                }
                    
                    // 重置流式消息
                    streamingMessage.value = null
                    sending.value = false
                    cleanupSSE()
                    return
                }

                // 更新流式消息内容
                if (data.message?.content) {
                    streamingMessage.value.content += data.message.content
                } 
                else if (data.content) {
                    streamingMessage.value.content += data.content
                }
                
                // 更新流式思考过程
                let thinkingUpdated = false
                if (data.message?.thinking) {
                    streamingMessage.value.thinking += data.message.thinking
                    thinkingUpdated = true
                }
                else if (data.thinking) {
                    streamingMessage.value.thinking += data.thinking
                    thinkingUpdated = true
                }
                
                nextTick(() => {
                    // 只有在内容更新且不是思考过程更新时才滚动到底部
                    if (!thinkingUpdated && !isThinkingToggleUpdate) {
                        scrollToBottom()
                    }
                })
            } catch (e) {
                console.error('解析流式数据失败:', e)
            }
        },
        (err) => {
            console.error('SSE错误:', err)
            // 错误处理：将当前流式内容作为消息保存
            if (streamingMessage.value && 
                (streamingMessage.value.content || streamingMessage.value.thinking)) {
                // 错误情况下也将思考内容设置为关闭状态
                messages.value.push({
                    id: generateId(),
                    role: 'assistant',
                    content: streamingMessage.value.content,
                    thinking: streamingMessage.value.thinking,
                    thinkingExpanded: false, // 思考完成后自动关闭
                    isStreaming: false
                })
            }
            streamingMessage.value = null
            sending.value = false
            cleanupSSE()
        },
        () => {
            // 完成回调
            if (streamingMessage.value && 
                (streamingMessage.value.content || streamingMessage.value.thinking)) {
                // 流式结束后，将思考内容设置为关闭状态
                messages.value.push({
                    id: generateId(),
                    role: 'assistant',
                    content: streamingMessage.value.content,
                    thinking: streamingMessage.value.thinking,
                    thinkingExpanded: false, // 思考完成后自动关闭
                    isStreaming: false
                })
            }
            streamingMessage.value = null
            sending.value = false
            cleanupSSE()
            nextTick(() => {
                scrollToBottom()
            })
        }
    )
}

// 监听路由变化
watch(
    () => route.params.sessionId,
    (newId) => {
        if (newId) {
            cleanupSSE()
            sessionId.value = newId
            messages.value = []
            streamingMessage.value = null
            fetchMessages()
        }
    },
    { immediate: true }
)

// 监听消息变化以滚动到底部
// 但避免在只切换思考过程显示状态时滚动
let isThinkingToggleUpdate = false

watch(
    () => allMessages.value,
    () => {
        nextTick(() => {
            // 如果是思考过程切换导致的更新，不滚动到底部
            if (!isThinkingToggleUpdate) {
                scrollToBottom()
            }
            // 重置标志
            isThinkingToggleUpdate = false
        })
    },
    { deep: true }
)

// 切换思考过程显示状态，避免点击时滚动到底部
const toggleThinking = (msg) => {
    if (!msg) return
    
    // 设置标志，告诉watch不要滚动到底部
    isThinkingToggleUpdate = true
    
    const currentExpanded = getThinkingExpanded(msg)
    
    // 保存当前滚动位置作为额外保障
    let currentScrollTop = 0
    if (messageContainer.value) {
        currentScrollTop = messageContainer.value.scrollTop
    }
    
    // 切换展开状态
    setThinkingExpanded(msg, !currentExpanded)
    
    // 恢复滚动位置作为额外保障
    nextTick(() => {
        if (messageContainer.value) {
            messageContainer.value.scrollTop = currentScrollTop
        }
    })
}

onMounted(() => {
    fetchMessages()
})

// Cleanup when component is unmounted
onUnmounted(() => {
    cleanupSSE()
})
</script>

<style lang="scss" scoped>
.chat-main {
    display: flex;
    height: 95vh;
    width: 100%;
}

.chat-window {
    flex: 1;
    display: grid;
    grid-template-rows: 84vh auto;
    background-color: rgba(255, 255, 255, 0.1);
    border-radius: 16px;
    overflow: hidden;
    font-family: 'Segoe UI', sans-serif;
    box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    backdrop-filter: blur(16px) saturate(1.5);
    -webkit-backdrop-filter: blur(16px) saturate(1.5);
    transition: all 0.3s ease;
}

.message-container {
    overflow-y: auto;
    padding: 1rem;
    background-color: #f9f9f954;

    &::-webkit-scrollbar {
        width: 6px;
    }

    &::-webkit-scrollbar-thumb {
        background-color: rgba(0, 0, 0, 0.2);
        border-radius: 3px;
    }
    
    .empty-state {
        text-align: center;
        color: #999;
        font-size: 1rem;
        padding: 2rem;
    }
}

.input-area {
    padding: 0.75rem 1rem;
    background-color: #fff;
    border-top: 1px solid #eaeaea;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);

    .input-wrapper {
        display: flex;
        align-items: end;
        gap: 10px;

        .el-textarea {
            flex: 1;

            .el-textarea__inner {
                resize: none;
                padding: 12px 20px;
                font-size: 1rem;
                line-height: 1.5;
                overflow-y: auto;
                border: 1px solid #dcdfe6;
                border-radius: 8px;
                outline: none;
                transition: all 0.3s ease;

                &:focus {
                    border-color: #409eff;
                    box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.2);
                }
            }
        }

        .send-button {
            flex: 0 0 auto;
            padding: 12px 20px;
            background-color: #409EFF;
            color: white;
            border-radius: 8px;
            white-space: nowrap;
            transition: background-color 0.3s ease;
            border: none;

            &:hover:not(:disabled) {
                background-color: #337ecc;
            }
            
            &:disabled {
                background-color: #a0cfff;
                cursor: not-allowed;
            }
        }
    }
}

.message-item {
    display: flex;
    flex-direction: column;
    margin-bottom: 1rem;
    animation: fadeIn 0.3s ease-in-out;
}

.message-header {
    display: flex;
    margin-bottom: 4px;

    .sender {
        font-size: 0.85rem;
        font-weight: 600;
        padding: 2px 8px;
        border-radius: 12px;
        background-color: transparent;
    }

    .sender.user {
        background-color: #409EFF;
        color: white;
        align-self: flex-end;
    }

    .sender.ai {
        background-color: #e0e0e0;
        color: #333;
    }
}

.content {
    max-width: 85%;
    padding: 0.75rem 1rem;
    border-radius: 18px;
    line-height: 1.5;
    word-wrap: break-word;
    font-size: 1rem;
    text-align: left;
    transition: background-color 0.3s;
}

.content.user {
    background-color: #409EFF;
    color: white;
    align-self: flex-end;
    margin-left: auto;
    border-bottom-right-radius: 4px;
}

.content.ai {
    background-color: #ffffff;
    color: #333;
    margin-right: auto;
    border-bottom-left-radius: 4px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}

.thinking-block {
    max-width: 85%;
    margin-right: auto;
    background-color: #f8f8f8;
    border-left: 4px solid #409eff;
    text-align: left;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
    box-sizing: border-box;
    transition: all 0.3s ease;

    .thinking-header {
        display: flex;
        align-items: center;
        gap: 8px;
        font-weight: 600;
        color: #409eff;
        padding: 0.25rem 0;
        cursor: pointer;
        user-select: none;
        transition: all 0.2s ease;
        position: relative;
        z-index: 10;
        width: 100%;
        box-sizing: border-box;

        &:hover {
            color: #337ecc;
            background-color: rgba(64, 158, 255, 0.1);
            padding: 0.25rem 0.5rem;
            margin: -0.25rem -0.5rem;
            border-radius: 4px;
        }
    }

    .thinking-toggle-icon {
        font-size: 0.75rem;
        transition: transform 0.2s ease;
        min-width: 12px;
        display: inline-block;
    }
    
    .streaming-indicator {
        margin-left: 8px;
        display: inline-flex;
        gap: 2px;
    }
    
    .dot {
        width: 6px;
        height: 6px;
        background-color: #409eff;
        border-radius: 50%;
        animation: pulse 1.4s infinite ease-in-out both;
    }
    
    .dot:nth-child(1) {
        animation-delay: -0.32s;
    }
    
    .dot:nth-child(2) {
        animation-delay: -0.16s;
    }
    
    @keyframes pulse {
        0%, 80%, 100% {
            transform: scale(0);
            opacity: 0.5;
        }
        40% {
            transform: scale(1);
            opacity: 1;
        }
    }

    .thinking-content-container {
        margin-top: 0.75rem;
        animation: slideDown 0.2s ease;
    }

    pre {
        white-space: pre-wrap;
        word-wrap: break-word;
        background-color: #f0f0f0;
        padding: 0.75rem;
        margin: 0;
        border-radius: 6px;
        overflow-x: auto;
        font-size: 0.85rem;
        line-height: 1.5;
        border: 1px solid #e0e0e0;
        box-sizing: border-box;
    }
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-5px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.markdown-content {
    p {
        margin: 0.5em 0;
    }

    pre {
        background-color: #f0f0f0;
        padding: 0.75rem;
        border-radius: 6px;
        overflow-x: auto;
        margin: 0.5em 0;
    }

    code {
        background-color: #eee;
        padding: 0.2em 0.4em;
        border-radius: 4px;
        font-family: monospace;
        font-size: 0.9em;
    }

    blockquote {
        border-left: 4px solid #409eff;
        padding-left: 1rem;
        color: #666;
        margin: 0.5em 0;
        background-color: rgba(64, 158, 255, 0.1);
        border-radius: 0 8px 8px 0;
    }

    ul,
    ol {
        padding-left: 1.5rem;
        margin: 0.5rem 0;
    }
    
    a {
        color: #409eff;
        text-decoration: none;
        
        &:hover {
            text-decoration: underline;
        }
    }
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}
</style>
```